什么是 LCP

LCP 测量的是视口内最大的图像或文本块的渲染时间,是 Core Web Vitals 三大核心指标之一。良好的 LCP 应该在 2.5 秒以内

LCP 候选元素类型

元素类型 说明
<img> 包括动画 GIF/PNG(使用首帧呈现时间)
<svg> 内的 <image> SVG 内嵌图像
<video> 使用海报图像或首帧时间(取较早者)
CSS background-image 通过 url() 加载的背景图
文本块 包含文本节点的块级元素

排除的元素

元素大小计算规则

可见大小 = 视口内可见部分的面积
场景 计算方式
图片 min(可见大小, 原始大小)
文本 能包含所有文本节点的最小矩形
超出视口 只计算视口内的部分
CSS 装饰 margin/padding/border 不计入

触发时机与更新规则

页面加载 → 首次绘制 → 持续监听 → 用户交互 → 停止
    ↓           ↓           ↓
  初始LCP    更新LCP    最终LCP

关键触发点

  1. 首次绘制时:浏览器派发初始 LCP 条目
  2. 发现更大元素时:派发新的 LCP 条目替换旧值
  3. 资源加载完成时:图片/字体加载前不算作已渲染,加载后重新计算
  4. 元素移除时:已移除的元素仍保持候选资格,除非发现更大元素

停止报告的条件

用户交互会终止 LCP 观察

交互类型 说明
keydown 键盘按键
click 鼠标点击
scroll 页面滚动
tap 触摸屏轻触

⚠️ 注意:web-vitals 库中没有监听 scroll,因为滚动可以被程序触发,不够可靠

页面状态变化也会触发最终报告

API 关键属性

const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];
  
  console.log({
    element: lastEntry.element,      // LCP 元素
    size: lastEntry.size,            // 元素大小
    startTime: lastEntry.startTime,  // 推荐使用此值作为 LCP
    renderTime: lastEntry.renderTime,// 渲染时间(跨域可能为 0)
    loadTime: lastEntry.loadTime,    // 加载时间
    url: lastEntry.url               // 图片 URL
  });
});

observer.observe({ type: "largest-contentful-paint", buffered: true });
小提示

startTime = renderTime || loadTime,建议使用 startTime 因为 renderTime 对跨域资源可能不可用

完整生命周期图

导航开始
   │
   ├─► 首个内容绘制 (FCP)
   │
   ├─► LCP 候选 #1 (小文本块)
   │
   ├─► LCP 候选 #2 (更大的图片) ← 替换 #1
   │
   ├─► 图片加载完成 → 重新计算大小
   │
   ├─► LCP 候选 #3 (Hero Image) ← 替换 #2
   │
   └─► 用户点击/滚动 → 停止观察,#3 成为最终 LCP

参考